#include "MySqlExprParser.h"
#include "../../../../utils/FnvHash.h"
#include "../../../../utils/instanceof.h"
#include "MySqlLexer.h"
#include "../../../ast/expr/SQLVariantRefExpr.h"
#include "../../../ast/expr/SQLMethodInvokeExpr.h"
#include "../../../ast/expr/SQLIdentifierExpr.h"
#include "../../../ast/SQLDataTypeImpl.h"
#include "../../../ast/SQLCurrentTimeExpr.h"
#include "../../../../Exception/IllegalArgumentException.h"
#include "../../../../utils/log.h"
#include "../../../../sql/ast/expr/SQLHexExpr.h"
#include "../../../../sql/ast/SQLPartition.h"
#include "../../../../sql/ast/SQLSubPartition.h"
#include "../../../ast/statement/SQLDDLStatement.h"
#include "../../../ast/SQLPartitionValue.h"
#include "MySqlSelectParser.h"

std::shared_ptr<std::vector<string_ptr>> MySqlExprParser::strings = std::make_shared<std::vector<string_ptr>>(std::vector<string_ptr>{
    make_string_ptr("AVG"),
    make_string_ptr("ANY_VALUE"),
    make_string_ptr("BIT_AND"),
    make_string_ptr("BIT_OR"),
    make_string_ptr("BIT_XOR"),
    make_string_ptr("COUNT"),
    make_string_ptr("GROUP_CONCAT"),
    make_string_ptr("LISTAGG"),
    make_string_ptr("MAX"),
    make_string_ptr("MIN"),
    make_string_ptr("STD"),
    make_string_ptr("STDDEV"),
    make_string_ptr("STDDEV_POP"),
    make_string_ptr("STDDEV_SAMP"),
    make_string_ptr("SUM"),
    make_string_ptr("VAR_SAMP"),
    make_string_ptr("VARIANCE"),
    make_string_ptr("JSON_ARRAYAGG"),
    make_string_ptr("JSON_OBJECTAGG")});

std::shared_ptr<std::vector<long>> MySqlExprParser::AGGREGATE_FUNCTIONS_CODES =
    FnvHash::fnv1a_64_lower(MySqlExprParser::strings, true);
// std::shared_ptr<std::vector<long>> MySqlExprParser::AGGREGATE_FUNCTIONS_CODES = std::make_shared<std::vector<long>>();
std::shared_ptr<std::vector<string_ptr>> MySqlExprParser::AGGREGATE_FUNCTIONS =
    std::make_shared<std::vector<string_ptr>>();

// https://dev.mysql.com/doc/refman/5.7/en/create-table.html
std::shared_ptr<std::vector<string_ptr>> MySqlExprParser::options =
    std::make_shared<std::vector<string_ptr>>(std::vector<string_ptr>{
        make_string_ptr("AUTO_INCREMENT"),
        make_string_ptr("AVG_ROW_LENGTH"),
        /*"CHARACTER SET",*/
        make_string_ptr("CHECKSUM"),
        make_string_ptr("COLLATE"),
        make_string_ptr("COMMENT"),
        make_string_ptr("COMPRESSION"),
        make_string_ptr("CONNECTION"),
        /*"{DATA|INDEX} DIRECTORY",*/
        make_string_ptr("DELAY_KEY_WRITE"),
        make_string_ptr("ENCRYPTION"),
        make_string_ptr("ENGINE"),
        make_string_ptr("INSERT_METHOD"),
        make_string_ptr("KEY_BLOCK_SIZE"),
        make_string_ptr("MAX_ROWS"),
        make_string_ptr("MIN_ROWS"),
        make_string_ptr("PACK_KEYS"),
        make_string_ptr("PASSWORD"),
        make_string_ptr("ROW_FORMAT"),
        make_string_ptr("STATS_AUTO_RECALC"),
        make_string_ptr("STATS_PERSISTENT"),
        make_string_ptr("STATS_SAMPLE_PAGES"),
        make_string_ptr("TABLESPACE"),
        make_string_ptr("UNION"),
        make_string_ptr("STORAGE_TYPE"),
        make_string_ptr("STORAGE_POLICY")});

std::shared_ptr<std::vector<long>> MySqlExprParser::SINGLE_WORD_TABLE_OPTIONS_CODES =
    FnvHash::fnv1a_64_lower(MySqlExprParser::options, true);
std::shared_ptr<std::vector<string_ptr>> MySqlExprParser::SINGLE_WORD_TABLE_OPTIONS =
    std::make_shared<std::vector<string_ptr>>();

BOOL_ptr MySqlExprParser::__init = MySqlExprParser::init();
BOOL_ptr MySqlExprParser::init()
{
  // FnvHash::fnv1a_64_lower(MySqlExprParser::strings, MySqlExprParser::AGGREGATE_FUNCTIONS_CODES, BOOL::TRUE);
  for (string_ptr str : *strings)
  {
    long hash = FnvHash::fnv1a_64_lower(str);
    // int index = Arrays.binarySearch(AGGREGATE_FUNCTIONS_CODES, hash);
    // int index = std::find(MySqlExprParser::AGGREGATE_FUNCTIONS_CODES, MySqlExprParser::AGGREGATE_FUNCTIONS_CODES + sizeof(MySqlExprParser::AGGREGATE_FUNCTIONS_CODES) / sizeof(long), hash) - MySqlExprParser::AGGREGATE_FUNCTIONS_CODES;
    int index = -1;
    int size = strings->size();
    // LOG_INFO << "size:" << size;
    // LOG_INFO << "MySqlExprParser::AGGREGATE_FUNCTIONS_CODES size:" << MySqlExprParser::AGGREGATE_FUNCTIONS_CODES->size();
    // for (size_t i=0; i < MySqlExprParser::AGGREGATE_FUNCTIONS_CODES->size();i++){
    //   LOG_INFO << "MySqlExprParser::AGGREGATE_FUNCTIONS_CODES->at("<<i<<"):" << MySqlExprParser::AGGREGATE_FUNCTIONS_CODES->at(i);
    // }
    // LOG_INFO << "str:" << str;
    for (size_t i = 0; i < AGGREGATE_FUNCTIONS_CODES->size(); i++)
    {
      AGGREGATE_FUNCTIONS->push_back(make_string_ptr(""));
    }
    for (int i = 0; i < size; i++)
    {
      // LOG_INFO << "i:" << i << ",size:" << size;
      if (MySqlExprParser::AGGREGATE_FUNCTIONS_CODES->at(i) == hash)
      {
        index = i;
        break;
      }
    }
    AGGREGATE_FUNCTIONS->at(index) = str;
  }

  for (size_t i = 0; i < SINGLE_WORD_TABLE_OPTIONS_CODES->size(); i++)
  {
    SINGLE_WORD_TABLE_OPTIONS->push_back(make_string_ptr(""));
  }
  for (string_ptr str : *options)
  {
    long hash = FnvHash::fnv1a_64_lower(str);
    // int index = Arrays.binarySearch(SINGLE_WORD_TABLE_OPTIONS_CODES, hash);
    // int index = std::find(MySqlExprParser::SINGLE_WORD_TABLE_OPTIONS_CODES, MySqlExprParser::SINGLE_WORD_TABLE_OPTIONS_CODES + sizeof(MySqlExprParser::SINGLE_WORD_TABLE_OPTIONS_CODES) / sizeof(long), hash) - MySqlExprParser::SINGLE_WORD_TABLE_OPTIONS_CODES;
    int index = -1;
    int size = options->size();
    for (int i = 0; i < size; i++)
    {
      // LOG_INFO << __FUNCTION__ << ": i:" << i << ",size:" << size;
      if (MySqlExprParser::SINGLE_WORD_TABLE_OPTIONS_CODES->at(i) == hash)
      {
        index = i;
        break;
      }
    }
    SINGLE_WORD_TABLE_OPTIONS->at(index) = str;
  }
}

MySqlExprParser::MySqlExprParser(Lexer_ptr lexer)
    : SQLExprParser(lexer, DbType_ptr(new DbType(&DbType::mysql)))
{

  this->aggregateFunctions = MySqlExprParser::AGGREGATE_FUNCTIONS;
  // memcpy(this->aggregateFunctions, MySqlExprParser::AGGREGATE_FUNCTIONS, sizeof(MySqlExprParser::AGGREGATE_FUNCTIONS));
  this->aggregateFunctionHashCodes = MySqlExprParser::AGGREGATE_FUNCTIONS_CODES;
}

MySqlExprParser::MySqlExprParser(string_ptr sql)
    : MySqlExprParser(MySqlLexer_ptr(new MySqlLexer(sql)))
{
  this->lexer->nextToken();
}

MySqlExprParser::MySqlExprParser(string_ptr sql, SQLParserFeature_list_ptr features)
    : SQLExprParser(MySqlLexer_ptr(new MySqlLexer(sql, features)), DbType_ptr(new DbType(&DbType::mysql)))
{
  // LOG_INFO << "dbType->name:" << dbType->name->c_str();
  dbType = DbType_ptr(new DbType(&DbType::mysql));
  this->aggregateFunctions = MySqlExprParser::AGGREGATE_FUNCTIONS;
  // memcpy(this->aggregateFunctions, MySqlExprParser::AGGREGATE_FUNCTIONS, sizeof(MySqlExprParser::AGGREGATE_FUNCTIONS));
  this->aggregateFunctionHashCodes = MySqlExprParser::AGGREGATE_FUNCTIONS_CODES;
  if (sql->length() > 6)
  {
    char c0 = sql->at(0);
    char c1 = sql->at(1);
    char c2 = sql->at(2);
    char c3 = sql->at(3);
    char c4 = sql->at(4);
    char c5 = sql->at(5);
    char c6 = sql->at(6);

    if (c0 == 'S' && c1 == 'E' && c2 == 'L' && c3 == 'E' && c4 == 'C' && c5 == 'T' && c6 == ' ')
    {
      lexer->reset(6, ' ', Token_ptr(new Token(&Token::SELECT)));
      return;
    }

    if (c0 == 's' && c1 == 'e' && c2 == 'l' && c3 == 'e' && c4 == 'c' && c5 == 't' && c6 == ' ')
    {
      lexer->reset(6, ' ', Token_ptr(new Token(&Token::SELECT)));
      return;
    }

    if (c0 == 'I' && c1 == 'N' && c2 == 'S' && c3 == 'E' && c4 == 'R' && c5 == 'T' && c6 == ' ')
    {
      lexer->reset(6, ' ', Token_ptr(new Token(&Token::INSERT)));
      return;
    }

    if (c0 == 'i' && c1 == 'n' && c2 == 's' && c3 == 'e' && c4 == 'r' && c5 == 't' && c6 == ' ')
    {
      lexer->reset(6, ' ', Token_ptr(new Token(&Token::INSERT)));
      return;
    }

    if (c0 == 'U' && c1 == 'P' && c2 == 'D' && c3 == 'A' && c4 == 'T' && c5 == 'E' && c6 == ' ')
    {
      lexer->reset(6, ' ', Token_ptr(new Token(&Token::UPDATE)));
      return;
    }

    if (c0 == 'u' && c1 == 'p' && c2 == 'd' && c3 == 'a' && c4 == 't' && c5 == 'e' && c6 == ' ')
    {
      lexer->reset(6, ' ', Token_ptr(new Token(&Token::UPDATE)));
      return;
    }

    if (c0 == '/' && c1 == '*' &&
        (isEnabled(SQLParserFeature_ptr(new SQLParserFeature(&SQLParserFeature::OptimizedForParameterized))) &&
         !isEnabled(SQLParserFeature_ptr(new SQLParserFeature(&SQLParserFeature::TDDLHint)))))
    {
      // MySqlLexer_ptr mySqlLexer = std::dynamic_pointer_cast<MySqlLexer>(lexer);
      auto mySqlLexer = static_cast<MySqlLexer *>(static_cast<void *>(lexer.get()));
      mySqlLexer->skipFirstHintsOrMultiCommentAndNextToken();
      return;
    }
  }
  LOG_INFO << "goto nextToken";
  this->lexer->nextToken();
  LOG_INFO << "token_:" << this->lexer->token_->name->c_str();
}

MySqlExprParser::MySqlExprParser(string_ptr sql, BOOL_ptr keepComments)
    : MySqlExprParser(MySqlLexer_ptr(new MySqlLexer(sql, BOOL::TRUE, keepComments)))
{
  this->lexer->nextToken();
}

MySqlExprParser::MySqlExprParser(string_ptr sql, BOOL_ptr skipComment, BOOL_ptr keepComments)
    : MySqlExprParser(MySqlLexer_ptr(new MySqlLexer(sql, skipComment, keepComments)))
{
  this->lexer->nextToken();
}

SQLExpr_ptr MySqlExprParser::primary()
{
  Token_ptr tok = lexer->token();
  LOG_INFO << "token name:" << tok->getName()->c_str();
  if (*tok == Token::IDENTIFIER)
  {
    long hash_lower = lexer->hashLCase();
    Lexer_SavePoint_ptr savePoint = lexer->mark();

    if (hash_lower == FnvHash::Constants::OUTLINE)
    {
      lexer->nextToken();
      try
      {
        SQLExpr_ptr file = primary();
        // SQLExpr_ptr expr = new MySqlOutFileExpr(file);

        // return primaryRest(expr);
      }
      catch (ParserException *e)
      {
        lexer->reset(savePoint);
      }
    }

    string_ptr strVal = make_string_ptr(encode_util::UnicodeToUTF8(*lexer->stringVal()));
    LOG_INFO << "leser text:" << strVal->c_str();

    bool quoteStart = strVal->length() > 0 && (strVal->at(0) == '`' || strVal->at(0) == '"');

    LOG_INFO << "quoteStart:" << quoteStart;
    if (!quoteStart)
    {
      // Allow function in order by when not start with '`'.
      SQLExprParser::setAllowIdentifierMethod(BOOL::TRUE);
    }

    std::shared_ptr<SQLCurrentTimeExpr> currentTimeExpr;
    currentTimeExpr = NULL;
    if (hash_lower == FnvHash::Constants::CURRENT_TIME && !quoteStart)
    {
      // currentTimeExpr = new SQLCurrentTimeExpr(SQLCurrentTimeExpr.Type.CURRENT_TIME);
    }
    else if (hash_lower == FnvHash::Constants::CURRENT_TIMESTAMP && !quoteStart)
    {
      // currentTimeExpr = new SQLCurrentTimeExpr(SQLCurrentTimeExpr.Type.CURRENT_TIMESTAMP);
    }
    else if (hash_lower == FnvHash::Constants::CURRENT_DATE && !quoteStart)
    {
      // currentTimeExpr = new SQLCurrentTimeExpr(SQLCurrentTimeExpr.Type.CURRENT_DATE);
    }
    else if (hash_lower == FnvHash::Constants::CURDATE && !quoteStart)
    {
      // currentTimeExpr = new SQLCurrentTimeExpr(SQLCurrentTimeExpr.Type.CURDATE);
    }
    else if (hash_lower == FnvHash::Constants::LOCALTIME && !quoteStart)
    {
      // currentTimeExpr = new SQLCurrentTimeExpr(SQLCurrentTimeExpr.Type.LOCALTIME);
    }
    else if (hash_lower == FnvHash::Constants::LOCALTIMESTAMP && !quoteStart)
    {
      // currentTimeExpr = new SQLCurrentTimeExpr(SQLCurrentTimeExpr.Type.LOCALTIMESTAMP);
    }
    else if (hash_lower == FnvHash::Constants::JSON_TABLE)
    {
      if (lexer->identifierEquals(L"JSON_TABLE"))
      {
        lexer->nextToken();
        accept(Token::LPAREN);

        // MySqlJSONTableExpr* jsonTable = new MySqlJSONTableExpr();
        // jsonTable.setExpr(this->expr());
        // accept(Token::COMMA);
        // jsonTable.setPath(this->expr());
        // acceptIdentifier("COLUMNS");
        // accept(Token::LPAREN);
        // for (; *lexer->token() != Token::RPAREN;)
        // {
        //   jsonTable.addColumn(parseJsonTableColumn());

        //   if (*lexer->token() == Token::COMMA)
        //   {
        //     lexer->nextToken();
        //     continue;
        //   }
        //   break;
        // }
        // accept(Token::RPAREN);

        // accept(Token::RPAREN);
        // return jsonTable;
      }
    }
    else if ((hash_lower == FnvHash::Constants::_LATIN1) && !quoteStart)
    {
      lexer->nextToken();

      string_ptr hexString;
      if (lexer->identifierEquals(FnvHash::Constants::X))
      {
        lexer->nextToken();
        hexString = make_string_ptr(encode_util::UnicodeToUTF8(*lexer->stringVal()));
        lexer->nextToken();
      }
      else if (*lexer->token() == Token::LITERAL_CHARS)
      {
        hexString->clear();
      }
      else
      {
        hexString = make_string_ptr(encode_util::UnicodeToUTF8(lexer->hexString()));
        lexer->nextToken();
      }

      SQLExpr_ptr charExpr;
      if (hexString->empty())
      {
        string_ptr str = make_string_ptr(encode_util::UnicodeToUTF8(*lexer->stringVal()));
        lexer->nextToken();

        string_ptr collate = NULL;
        if (lexer->identifierEquals(FnvHash::Constants::COLLATE))
        {
          lexer->nextToken();
          collate = make_string_ptr(encode_util::UnicodeToUTF8(*lexer->stringVal()));
          if (*lexer->token() == Token::LITERAL_CHARS)
          {
            lexer->nextToken();
          }
          else
          {
            accept(Token::IDENTIFIER);
          }
        }

        // charExpr = new MySqlCharExpr(str, "_latin1", collate);
      }
      else
      {
        // charExpr = new MySqlCharExpr(hexString, "_latin1");
      }

      return primaryRest(charExpr);
    }
    else if ((hash_lower == FnvHash::Constants::_UTF8 ||
              hash_lower == FnvHash::Constants::_UTF8MB4) &&
             !quoteStart)
    {
      lexer->nextToken();

      string_ptr hexString;
      if (lexer->identifierEquals(FnvHash::Constants::X))
      {
        lexer->nextToken();
        hexString = make_string_ptr(encode_util::UnicodeToUTF8(*lexer->stringVal()));
        lexer->nextToken();
      }
      else if (*lexer->token() == Token::LITERAL_CHARS)
      {
        hexString->clear();
      }
      else
      {
        hexString = make_string_ptr(encode_util::UnicodeToUTF8(lexer->hexString()));
        lexer->nextToken();
      }

      SQLExpr_ptr charExpr;
      if (hexString->empty())
      {
        string_ptr str = make_string_ptr(encode_util::UnicodeToUTF8(*lexer->stringVal()));
        lexer->nextToken();

        string_ptr collate = NULL;
        if (lexer->identifierEquals(FnvHash::Constants::COLLATE))
        {
          lexer->nextToken();
          collate = make_string_ptr(encode_util::UnicodeToUTF8(*lexer->stringVal()));
          if (*lexer->token() == Token::LITERAL_CHARS)
          {
            lexer->nextToken();
          }
          else
          {
            accept(Token::IDENTIFIER);
          }
        }

        // charExpr = new MySqlCharExpr(str, "_utf8", collate);
      }
      else
      {
        // string_ptr  str = MySqlUtils.utf8(hexString);
        // charExpr = new SQLCharExpr(str);
        charExpr = SQLCharExpr_ptr(new SQLCharExpr(hexString));
      }

      return primaryRest(charExpr);
    }
    else if ((hash_lower == FnvHash::Constants::_UTF16 ||
              hash_lower == FnvHash::Constants::_UCS2) &&
             !quoteStart)
    {
      lexer->nextToken();

      string_ptr hexString;
      if (lexer->identifierEquals(FnvHash::Constants::X))
      {
        lexer->nextToken();
        hexString = make_string_ptr(encode_util::UnicodeToUTF8(*lexer->stringVal()));
        lexer->nextToken();
      }
      else if (*lexer->token() == Token::LITERAL_CHARS)
      {
        hexString->clear();
      }
      else
      {
        hexString = make_string_ptr(encode_util::UnicodeToUTF8(lexer->hexString()));
        lexer->nextToken();
      }

      SQLCharExpr_ptr charExpr;
      if (hexString->empty())
      {
        string_ptr str = make_string_ptr(encode_util::UnicodeToUTF8(*lexer->stringVal()));
        lexer->nextToken();
        // charExpr = new MySqlCharExpr(str, "_utf16");
      }
      else
      {
        // charExpr = new SQLCharExpr(MySqlUtils.utf16(hexString));
      }

      return primaryRest(charExpr);
    }
    else if ((hash_lower == FnvHash::Constants::_UTF16LE) && !quoteStart)
    {
      lexer->nextToken();

      string_ptr hexString;
      if (lexer->identifierEquals(FnvHash::Constants::X))
      {
        lexer->nextToken();
        hexString = make_string_ptr(encode_util::UnicodeToUTF8(*lexer->stringVal()));
        lexer->nextToken();
      }
      else if (*lexer->token() == Token::LITERAL_CHARS)
      {
        hexString->clear();
      }
      else
      {
        hexString = make_string_ptr(encode_util::UnicodeToUTF8(lexer->hexString()));
        lexer->nextToken();
      }

      SQLCharExpr_ptr charExpr;
      if (hexString->empty())
      {
        string_ptr str = make_string_ptr(encode_util::UnicodeToUTF8(*lexer->stringVal()));
        lexer->nextToken();
        // charExpr = new MySqlCharExpr(str, "_utf16le");
      }
      else
      {
        // charExpr = new MySqlCharExpr(hexString, "_utf16le");
      }

      return primaryRest(charExpr);
    }
    else if (hash_lower == FnvHash::Constants::_UTF32 && !quoteStart)
    {
      lexer->nextToken();

      string_ptr hexString;
      if (lexer->identifierEquals(FnvHash::Constants::X))
      {
        lexer->nextToken();
        hexString = make_string_ptr(encode_util::UnicodeToUTF8(*lexer->stringVal()));
        lexer->nextToken();
      }
      else if (*lexer->token() == Token::LITERAL_CHARS)
      {
        hexString->clear();
      }
      else
      {
        hexString = make_string_ptr(encode_util::UnicodeToUTF8(lexer->hexString()));
        lexer->nextToken();
      }

      SQLCharExpr_ptr charExpr;
      if (hexString->empty())
      {
        string_ptr str = make_string_ptr(encode_util::UnicodeToUTF8(*lexer->stringVal()));
        lexer->nextToken();
        // charExpr = new MySqlCharExpr(str, "_utf32");
      }
      else
      {
        // charExpr = new SQLCharExpr(MySqlUtils.utf32(hexString));
      }

      return primaryRest(charExpr);
    }
    else if (hash_lower == FnvHash::Constants::_GBK && !quoteStart)
    {
      lexer->nextToken();

      string_ptr hexString;
      if (lexer->identifierEquals(FnvHash::Constants::X))
      {
        lexer->nextToken();
        hexString = make_string_ptr(encode_util::UnicodeToUTF8(*lexer->stringVal()));
        lexer->nextToken();
      }
      else if (*lexer->token() == Token::LITERAL_CHARS)
      {
        hexString->clear();
      }
      else
      {
        hexString = make_string_ptr(encode_util::UnicodeToUTF8(lexer->hexString()));
        lexer->nextToken();
      }

      SQLCharExpr_ptr charExpr;
      if (hexString->empty())
      {
        string_ptr str = make_string_ptr(encode_util::UnicodeToUTF8(*lexer->stringVal()));
        lexer->nextToken();
        // charExpr = new MySqlCharExpr(str, "_gbk");
      }
      else
      {
        // charExpr = new SQLCharExpr(MySqlUtils.gbk(hexString));
      }

      return primaryRest(charExpr);
    }
    else if (hash_lower == FnvHash::Constants::_UJIS && !quoteStart)
    {
      lexer->nextToken();

      string_ptr hexString;
      if (lexer->identifierEquals(FnvHash::Constants::X))
      {
        lexer->nextToken();
        hexString = make_string_ptr(encode_util::UnicodeToUTF8(*lexer->stringVal()));
        lexer->nextToken();
      }
      else if (*lexer->token() == Token::LITERAL_CHARS)
      {
        hexString->clear();
      }
      else
      {
        hexString = make_string_ptr(encode_util::UnicodeToUTF8(lexer->hexString()));
        lexer->nextToken();
      }

      SQLCharExpr_ptr charExpr;
      if (hexString->empty())
      {
        string_ptr str = make_string_ptr(encode_util::UnicodeToUTF8(*lexer->stringVal()));
        lexer->nextToken();
        // charExpr = new MySqlCharExpr(str, "_ujis");
      }
      else
      {
        // charExpr = new MySqlCharExpr(hexString, "_ujis");
      }

      return primaryRest(charExpr);
    }
    else if (hash_lower == FnvHash::Constants::_BIG5 && !quoteStart)
    {
      lexer->nextToken();

      string_ptr hexString;
      if (lexer->identifierEquals(FnvHash::Constants::X))
      {
        lexer->nextToken();
        hexString = make_string_ptr(encode_util::UnicodeToUTF8(*lexer->stringVal()));
        lexer->nextToken();
      }
      else if (*lexer->token() == Token::LITERAL_CHARS)
      {
        hexString->clear();
      }
      else
      {
        hexString = make_string_ptr(encode_util::UnicodeToUTF8(lexer->hexString()));
        lexer->nextToken();
      }

      SQLCharExpr_ptr charExpr;
      if (hexString->empty())
      {
        string_ptr str = make_string_ptr(encode_util::UnicodeToUTF8(*lexer->stringVal()));
        lexer->nextToken();
        // charExpr = new MySqlCharExpr(str, "_big5");
      }
      else
      {
        // charExpr = new SQLCharExpr(MySqlUtils.big5(hexString));
      }

      return primaryRest(charExpr);
    }
    else if (hash_lower == FnvHash::Constants::CURRENT_USER &&
             isEnabled(SQLParserFeature_ptr(new SQLParserFeature(&SQLParserFeature::EnableCurrentUserExpr))))
    {
      lexer->nextToken();
      // return primaryRest(new SQLCurrentUserExpr());
    }
    else if (hash_lower == -5808529385363204345L && lexer->charAt(lexer->pos()) == L'\'')
    { // hex
      lexer->nextToken();
      string_ptr tmp = make_string_ptr(encode_util::UnicodeToUTF8(*lexer->stringVal()));
      SQLHexExpr_ptr hex = SQLHexExpr_ptr(new SQLHexExpr(tmp));
      lexer->nextToken();
      return primaryRest(hex);
    }

    if (currentTimeExpr != NULL)
    {
      LOG_INFO << "currentTimeExpr != NULL";
      string_ptr methodName = make_string_ptr(encode_util::UnicodeToUTF8(*lexer->stringVal()));
      lexer->nextToken();

      if (*lexer->token() == Token::LPAREN)
      {
        lexer->nextToken();
        if (*lexer->token() == Token::LPAREN)
        {
          lexer->nextToken();
        }
        else
        {
          return primaryRest(
              methodRest(SQLIdentifierExpr_ptr(new SQLIdentifierExpr((methodName))), BOOL::FALSE));
        }
      }

      return primaryRest(currentTimeExpr);
    }

    return SQLExprParser::primary();
  }
  else if (*tok == Token::VARIANT)
  {
    string_ptr tmp = make_string_ptr(encode_util::UnicodeToUTF8(*lexer->stringVal()));
    SQLVariantRefExpr_ptr varRefExpr = SQLVariantRefExpr_ptr(new SQLVariantRefExpr(tmp));
    lexer->nextToken();
    if (StringUtils::equalsIgnoreCase(varRefExpr->getName(), make_string_ptr("@@global")))
    {
      accept(Token::DOT);
      string_ptr tmp1 = make_string_ptr(encode_util::UnicodeToUTF8(*lexer->stringVal()));
      varRefExpr = SQLVariantRefExpr_ptr(new SQLVariantRefExpr(tmp1, BOOL::TRUE));
      lexer->nextToken();
    }
    else if ((*varRefExpr->getName() == "@") && *lexer->token() == Token::LITERAL_CHARS)
    {
      string_ptr tmp1 = make_string_ptr("@'" + encode_util::UnicodeToUTF8(*lexer->stringVal()) + "'");
      varRefExpr->setName(tmp1);
      lexer->nextToken();
    }
    else if ((*varRefExpr->getName() == "@@") && *lexer->token() == Token::LITERAL_CHARS)
    {
      string_ptr tmp1 = make_string_ptr("@@'" + encode_util::UnicodeToUTF8(*lexer->stringVal()) + "'");
      varRefExpr->setName(tmp1);
      lexer->nextToken();
    }
    return primaryRest(varRefExpr);
  }
  else if (*tok == Token::VALUES)
  {
    lexer->nextToken();

    if (*lexer->token() != Token::LPAREN)
    {
      SQLExpr_ptr expr = primary();
      // SQLValuesQuery* values = new SQLValuesQuery();
      // values.addValue(new SQLListExpr(expr));
      // return new SQLQueryExpr(new SQLSelect(values));
    }
    // return this->methodRest(new SQLIdentifierExpr("VALUES"), BOOL::TRUE);
  }
  else if (*tok == Token::BINARY)
  {
    lexer->nextToken();
    if (*lexer->token() == Token::COMMA || *lexer->token() == Token::SEMI || *lexer->token() == Token::XEOF)
    {
      // return new SQLIdentifierExpr("BINARY");
    }
    else
    {
      // SQLUnaryExpr* binaryExpr = new SQLUnaryExpr(SQLUnaryOperator::BINARY, primary());
      // return primaryRest(binaryExpr);
    }
  }
  else
  {
    try
    {
      return SQLExprParser::primary();
    }
    catch (const std::exception &e)
    {
      LOG_INFO << "catch:" << e.what();
    }
    catch (...)
    {
      LOG_INFO << "catch:";
    }
    return nullptr;
  }
}

// MySqlJSONTableExpr::Column* MySqlExprParser::parseJsonTableColumn()
// {
//   MySqlJSONTableExpr::Column* column = new MySqlJSONTableExpr::Column();

//   SQLName name = this->name();
//   column->setName(
//       name);

//   if (*lexer->token() == Token::FOR)
//   {
//     lexer->nextToken();
//     acceptIdentifier("ORDINALITY");
//   }
//   else
//   {
//     BOOL_ptr nested = name instanceof SQLIdentifierExpr && name.nameHashCode64() == FnvHash::Constants::NESTED;

//     if (!nested)
//     {
//       column->setDataType(
//           this->parseDataType());
//     }

//     if (*lexer->token() == Token::EXISTS)
//     {
//       lexer->nextToken();
//       column->setExists(BOOL::TRUE);
//     }

//     if (lexer->identifierEquals(FnvHash::Constants::PATH))
//     {
//       lexer->nextToken();
//       column->setPath(
//           this->primary());
//     }

//     if (name instanceof SQLIdentifierExpr && name.nameHashCode64() == FnvHash::Constants::NESTED)
//     {
//       acceptIdentifier("COLUMNS");
//       accept(Token::LPAREN);
//       for (; lexer->token() != Token::RPAREN;)
//       {
//         MySqlJSONTableExpr.Column nestedColumn = parseJsonTableColumn();
//         column->addNestedColumn(nestedColumn);

//         if (*lexer->token() == Token::COMMA)
//         {
//           lexer->nextToken();
//           continue;
//         }
//         break;
//       }
//       accept(Token::RPAREN);
//     }

//     for (int i = 0; i < 2; ++i)
//     {
//       if (lexer->identifierEquals("ERROR") || lexer->token() == Token::DEFAULT || lexer->token() == Token::NULL)
//       {
//         if (*lexer->token() == Token::DEFAULT)
//         {
//           lexer->nextToken();
//         }

//         SQLExpr_ptr expr = this->expr();
//         accept(Token::ON);
//         if (lexer->identifierEquals("ERROR"))
//         {
//           lexer->nextToken();
//           column->setOnError(expr);
//         }
//         else
//         {
//           acceptIdentifier("EMPTY");
//           column->setOnEmpty(expr);
//         }
//       }
//     }
//   }

//   return column;
// }

SQLExpr_ptr MySqlExprParser::primaryRest(SQLExpr_ptr expr)
{
  LOG_DEBUG << "in primaryRest";
  if (expr == NULL)
  {
    throw new IllegalArgumentException(make_string_ptr("expr"));
  }

  LOG_DEBUG << "token name:" << lexer->token()->name->c_str();
  if (*lexer->token() == Token::LITERAL_CHARS)
  {
    if (instanceof <SQLExpr, SQLIdentifierExpr>(expr))
    {
      SQLIdentifierExpr_ptr identExpr = std::dynamic_pointer_cast<SQLIdentifierExpr>(expr);
      string_ptr ident = identExpr->getName();

      if (StringUtils::equalsIgnoreCase(ident, make_string_ptr("x")))
      {
        wchar_t ch = lexer->charAt(lexer->pos());
        if (ch == L'\'')
        {
          string_ptr charValue = make_string_ptr(encode_util::UnicodeToUTF8(*lexer->stringVal()));
          lexer->nextToken();
          expr = SQLHexExpr_ptr(new SQLHexExpr(charValue));
          return primaryRest(expr);
        }

        //                } else if (ident.equalsIgnoreCase("b")) {
        //                    string_ptr  charValue = lexer->stringVal();
        //                    lexer->nextToken();
        //                    expr = new SQLBinaryExpr(charValue);
        //
        //                    return primaryRest(expr);
      }
      else if (StringUtils::startWith(ident, make_string_ptr("_")))
      {
        string_ptr charValue = make_string_ptr(encode_util::UnicodeToUTF8(*lexer->stringVal()));
        lexer->nextToken();

        // MySqlCharExpr mysqlCharExpr = new MySqlCharExpr(charValue);
        // mysqlCharExpr.setCharset(identExpr.getName());
        if (lexer->identifierEquals(FnvHash::Constants::COLLATE))
        {
          lexer->nextToken();

          string_ptr collate = make_string_ptr(encode_util::UnicodeToUTF8(*lexer->stringVal()));
          // mysqlCharExpr.setCollate(collate);
          if (*lexer->token() == Token::LITERAL_CHARS)
          {
            lexer->nextToken();
          }
          else
          {
            accept(Token::IDENTIFIER);
          }
        }

        // expr = mysqlCharExpr;

        return primaryRest(expr);
      }
    }
    else if (instanceof <SQLExpr, SQLCharExpr>(expr))
    {
      string_ptr text2 = (std::dynamic_pointer_cast<SQLCharExpr>(expr))->getText();
      do
      {
        string_ptr chars = make_string_ptr(encode_util::UnicodeToUTF8(*lexer->stringVal()));
        text2->append(*chars);
        lexer->nextToken();
      } while (*lexer->token() == Token::LITERAL_CHARS || *lexer->token() == Token::LITERAL_ALIAS);
      expr = SQLCharExpr_ptr(new SQLCharExpr(text2));
    }
    else if (instanceof <SQLExpr, SQLVariantRefExpr>(expr))
    {
      string_ptr tmp = make_string_ptr("CONCAT");
      SQLMethodInvokeExpr_ptr concat = SQLMethodInvokeExpr_ptr(new SQLMethodInvokeExpr(tmp));
      concat->addArgument(expr);
      concat->addArgument(this->primary());
      expr = concat;

      return primaryRest(expr);
    }
  }
  else if (*lexer->token() == Token::IDENTIFIER)
  {
    if (instanceof <SQLExpr, SQLHexExpr>(expr))
    {
      if (StringUtils::equalsIgnoreCase(make_string_ptr("USING"), make_string_ptr(encode_util::UnicodeToUTF8(*lexer->stringVal()))))
      {
        lexer->nextToken();
        if (*lexer->token() != Token::IDENTIFIER)
        {
          throw new ParserException(make_string_ptr("syntax error, illegal hex. " + encode_util::UnicodeToUTF8(lexer->info())));
        }
        string_ptr charSet = make_string_ptr(encode_util::UnicodeToUTF8(*lexer->stringVal()));
        lexer->nextToken();
        // char *cstr = const_cast<char *>(charSet->c_str());
        Object_ptr tmpObject = Object_ptr(new Object());
        tmpObject->any_data_ = std::string{charSet->c_str()};
        expr->getAttributes()->insert(std::make_pair("USING", tmpObject));

        return primaryRest(expr);
      }
    }
    else if (lexer->identifierEquals(FnvHash::Constants::COLLATE))
    {
      lexer->nextToken();

      if (*lexer->token() == Token::EQ)
      {
        lexer->nextToken();
      }

      if (*lexer->token() != Token::IDENTIFIER && *lexer->token() != Token::LITERAL_CHARS)
      {
        throw new ParserException(make_string_ptr("syntax error. " + encode_util::UnicodeToUTF8(lexer->info())));
      }

      string_ptr collate = make_string_ptr(encode_util::UnicodeToUTF8(*lexer->stringVal()));
      lexer->nextToken();

      SQLBinaryOpExpr_ptr binaryExpr =
          SQLBinaryOpExpr_ptr(new SQLBinaryOpExpr(
              expr,
              SQLBinaryOperator_ptr(new SQLBinaryOperator(&SQLBinaryOperator::COLLATE)),
              SQLIdentifierExpr_ptr(new SQLIdentifierExpr((collate))),
              DbType_ptr(new DbType(&DbType::mysql))));

      expr = binaryExpr;

      return primaryRest(expr);
    }
    else if (instanceof <SQLExpr, SQLVariantRefExpr>(expr))
    {
      if (lexer->identifierEquals(FnvHash::Constants::COLLATE))
      {
        lexer->nextToken();

        if (*lexer->token() != Token::IDENTIFIER && *lexer->token() != Token::LITERAL_CHARS)
        {
          throw new ParserException(make_string_ptr("syntax error. " + encode_util::UnicodeToUTF8(lexer->info())));
        }

        string_ptr collate = make_string_ptr(encode_util::UnicodeToUTF8(*lexer->stringVal()));
        lexer->nextToken();

        // char *cstr = const_cast<char *>(collate->c_str());
        Object_ptr tmpObject = Object_ptr(new Object());
        tmpObject->any_data_ = std::string{collate->c_str()};
        expr->putAttribute("COLLATE", tmpObject);

        return primaryRest(expr);
      }
    }
  }
  else if (*lexer->token() == Token::LBRACKET)
  {
    // SQLArrayExpr* array = new SQLArrayExpr();
    // array->setExpr(expr);
    lexer->nextToken();
    // this->exprList(array.getValues(), array);
    accept(Token::RBRACKET);
    // return primaryRest(array);
  }

  LOG_DEBUG << "token name:" << lexer->token()->name->c_str();
  if (*lexer->token() == Token::VARIANT)
  {
    string_ptr variant = make_string_ptr(encode_util::UnicodeToUTF8(*lexer->stringVal()));
    if ("@" == *variant)
    {
      return userNameRest(expr);
    }
    else if ("@localhost" == *variant)
    {
      return userNameRest(expr);
    }
    else
    {
      throw new ParserException(make_string_ptr("syntax error. " + encode_util::UnicodeToUTF8(lexer->info())));
    }
  }

  LOG_DEBUG << "token name:" << lexer->token()->name->c_str();
  if (*lexer->token() == Token::ERROR)
  {
    throw new ParserException(make_string_ptr("syntax error. " + encode_util::UnicodeToUTF8(lexer->info())));
  }

  LOG_DEBUG << "goto SQLExprParser::primaryRest";
  return SQLExprParser::primaryRest(expr);
}

SQLName_ptr MySqlExprParser::userName()
{
  SQLName_ptr name = this->name();
  if (*lexer->token() == Token::LPAREN && name->hashCode64() == FnvHash::Constants::CURRENT_USER)
  {
    lexer->nextToken();
    accept(Token::RPAREN);
    return name;
  }

  return std::dynamic_pointer_cast<SQLName>(userNameRest(name));
}

SQLExpr_ptr MySqlExprParser::userNameRest(SQLExpr_ptr expr)
{
  if (*lexer->token() != Token::VARIANT || !StringUtils::startWith(make_string_ptr(encode_util::UnicodeToUTF8(*lexer->stringVal())), make_string_ptr("@")))
  {
    return expr;
  }

  // MySqlUserName* userName = new MySqlUserName();
  // if (expr instanceof SQLCharExpr)
  // {
  //   userName.setUserName(((SQLCharExpr)expr).getText());
  // }
  // else
  // {
  //   userName.setUserName(((SQLIdentifierExpr)expr).getName());
  // }

  string_ptr strVal = make_string_ptr(encode_util::UnicodeToUTF8(*lexer->stringVal()));
  lexer->nextToken();

  if (strVal->length() > 1)
  {
    // userName.setHost(strVal.substring(1));
    // return userName;
  }

  if (*lexer->token() == Token::LITERAL_CHARS)
  {
    // userName.setHost(lexer->stringVal());
  }
  else
  {
    if (*lexer->token() == Token::PERCENT)
    {
      throw new ParserException(make_string_ptr("syntax error. " + encode_util::UnicodeToUTF8(lexer->info())));
    }
    else
    {
      // userName.setHost(lexer->stringVal());
    }
  }
  lexer->nextToken();

  if (lexer->identifierEquals(FnvHash::Constants::IDENTIFIED))
  {
    Lexer_SavePoint_ptr mark = lexer->mark();

    lexer->nextToken();
    if (*lexer->token() == Token::BY)
    {
      lexer->nextToken();
      if (lexer->identifierEquals(FnvHash::Constants::PASSWORD))
      {
        lexer->reset(mark);
      }
      else
      {
        // userName->setIdentifiedBy(lexer->stringVal());
        lexer->nextToken();
      }
    }
    else
    {
      lexer->reset(mark);
    }
  }

  // return userName;
  return NULL;
}

SQLExpr_ptr MySqlExprParser::parsePosition()
{
  SQLExpr_ptr expr = this->primary();
  expr = this->primaryRest(expr);
  expr = bitXorRest(expr);
  expr = additiveRest(expr);
  expr = shiftRest(expr);
  expr = bitAndRest(expr);
  expr = bitOrRest(expr);

  if (*lexer->token() == Token::IN)
  {
    accept(Token::IN);
  }
  else if (*lexer->token() == Token::COMMA)
  {
    accept(Token::COMMA);
  }
  else
  {
    throw new ParserException(make_string_ptr("syntax error. " + encode_util::UnicodeToUTF8(lexer->info())));
  }
  SQLExpr_ptr str = this->getExpr();
  accept(Token::RPAREN);

  string_ptr tmp = make_string_ptr("LOCATE");
  SQLMethodInvokeExpr_ptr locate = SQLMethodInvokeExpr_ptr(new SQLMethodInvokeExpr(tmp));
  locate->addArgument(expr);
  locate->addArgument(str);

  return primaryRest(locate);
}

SQLExpr_ptr MySqlExprParser::parseExtract()
{
  SQLExpr_ptr expr;
  if (*lexer->token() != Token::IDENTIFIER)
  {
    throw new ParserException(make_string_ptr("syntax error. " + encode_util::UnicodeToUTF8(lexer->info())));
  }

  string_ptr unitVal = make_string_ptr(encode_util::UnicodeToUTF8(*lexer->stringVal()));
  // SQLIntervalUnit* unit = SQLIntervalUnit::valueOf(unitVal.toUpperCase());
  lexer->nextToken();

  accept(Token::FROM);

  SQLExpr_ptr value = getExpr();

  // SQLExtractExpr extract = new SQLExtractExpr();
  // extract.setValue(value);
  // extract.setUnit(unit);
  accept(Token::RPAREN);

  // expr = extract;

  return primaryRest(expr);
}

SQLSelectParser_ptr MySqlExprParser::createSelectParser()
{
  return MySqlSelectParser_ptr(new MySqlSelectParser(SharedObject(MySqlExprParser)));
}

SQLExpr_ptr MySqlExprParser::parseInterval()
{
  accept(Token::INTERVAL);

  if (*lexer->token() == Token::LPAREN)
  {
    lexer->nextToken();

    string_ptr tmp = make_string_ptr("INTERVAL");
    SQLMethodInvokeExpr_ptr methodInvokeExpr = SQLMethodInvokeExpr_ptr(new SQLMethodInvokeExpr(tmp));
    if (*lexer->token() != Token::RPAREN)
    {
      exprList(methodInvokeExpr->getArguments(), methodInvokeExpr);
    }

    accept(Token::RPAREN);

    if (methodInvokeExpr->getArguments()->size() == 1 && *lexer->token() == Token::IDENTIFIER)
    {
      SQLExpr_ptr value = (*methodInvokeExpr->getArguments()->begin());
      string_ptr unit = make_string_ptr(encode_util::UnicodeToUTF8(*lexer->stringVal()));
      lexer->nextToken();

      // SQLIntervalExpr* intervalExpr = new SQLIntervalExpr();
      // intervalExpr.setValue(value);
      // intervalExpr.setUnit(SQLIntervalUnit::valueOf(unit.toUpperCase()));
      // return intervalExpr;
    }
    else
    {
      return primaryRest(methodInvokeExpr);
    }
  }
  else
  {
    SQLExpr_ptr value = getExpr();

    if (*lexer->token() != Token::IDENTIFIER)
    {
      throw new ParserException(make_string_ptr("Syntax error. " + encode_util::UnicodeToUTF8(lexer->info())));
    }

    // SQLIntervalUnit* intervalUnit;

    string_ptr unit = make_string_ptr(encode_util::UnicodeToUTF8(*lexer->stringVal()));
    long unitHash = lexer->hashLCase();
    lexer->nextToken();

    // intervalUnit = SQLIntervalUnit.valueOf(unit.toUpperCase());
    if (*lexer->token() == Token::TO)
    {
      lexer->nextToken();
      if (unitHash == FnvHash::Constants::YEAR)
      {
        if (lexer->identifierEquals(FnvHash::Constants::MONTH))
        {
          lexer->nextToken();
          // intervalUnit = SQLIntervalUnit.YEAR_MONTH;
        }
        else
        {
          throw new ParserException(make_string_ptr("Syntax error. " + encode_util::UnicodeToUTF8(lexer->info())));
        }
      }
      else if (unitHash == FnvHash::Constants::DAY)
      {
        if (lexer->identifierEquals(FnvHash::Constants::HOUR))
        {
          lexer->nextToken();
          // intervalUnit = SQLIntervalUnit.DAY_HOUR;
        }
        else if (lexer->identifierEquals(FnvHash::Constants::MINUTE))
        {
          lexer->nextToken();
          // intervalUnit = SQLIntervalUnit.DAY_MINUTE;
        }
        else if (lexer->identifierEquals(FnvHash::Constants::SECOND))
        {
          lexer->nextToken();
          // intervalUnit = SQLIntervalUnit.DAY_SECOND;
        }
        else if (lexer->identifierEquals(FnvHash::Constants::MICROSECOND))
        {
          lexer->nextToken();
          // intervalUnit = SQLIntervalUnit.DAY_MICROSECOND;
        }
        else
        {
          throw new ParserException(make_string_ptr("Syntax error. " + encode_util::UnicodeToUTF8(lexer->info())));
        }
      }
      else if (unitHash == FnvHash::Constants::HOUR)
      {
        if (lexer->identifierEquals(FnvHash::Constants::MINUTE))
        {
          lexer->nextToken();
          // intervalUnit = SQLIntervalUnit.HOUR_MINUTE;
        }
        else if (lexer->identifierEquals(FnvHash::Constants::SECOND))
        {
          lexer->nextToken();
          // intervalUnit = SQLIntervalUnit.HOUR_SECOND;
        }
        else if (lexer->identifierEquals(FnvHash::Constants::MICROSECOND))
        {
          lexer->nextToken();
          // intervalUnit = SQLIntervalUnit.HOUR_MICROSECOND;
        }
        else
        {
          throw new ParserException(make_string_ptr("Syntax error. " + encode_util::UnicodeToUTF8(lexer->info())));
        }
      }
      else if (unitHash == FnvHash::Constants::MINUTE)
      {
        if (lexer->identifierEquals(FnvHash::Constants::SECOND))
        {
          lexer->nextToken();
          // intervalUnit = SQLIntervalUnit.MINUTE_SECOND;
        }
        else if (lexer->identifierEquals(FnvHash::Constants::MICROSECOND))
        {
          lexer->nextToken();
          // intervalUnit = SQLIntervalUnit.MINUTE_MICROSECOND;
        }
        else
        {
          throw new ParserException(make_string_ptr("Syntax error. " + encode_util::UnicodeToUTF8(lexer->info())));
        }
      }
      else if (unitHash == FnvHash::Constants::SECOND)
      {
        if (lexer->identifierEquals(FnvHash::Constants::MICROSECOND))
        {
          lexer->nextToken();
          // intervalUnit = SQLIntervalUnit.SECOND_MICROSECOND;
        }
        else
        {
          throw new ParserException(make_string_ptr("Syntax error. " + encode_util::UnicodeToUTF8(lexer->info())));
        }
      }
      else
      {
        throw new ParserException(make_string_ptr("Syntax error. " + encode_util::UnicodeToUTF8(lexer->info())));
      }
    }

    // SQLIntervalExpr intervalExpr = new SQLIntervalExpr();
    // intervalExpr.setValue(value);
    // intervalExpr.setUnit(intervalUnit);

    // return intervalExpr;
    return NULL;
  }
}

SQLColumnDefinition_ptr MySqlExprParser::parseColumn()
{
  SQLColumnDefinition_ptr column = SQLColumnDefinition_ptr(new SQLColumnDefinition());
  column->setDbType(dbType);

  SQLName_ptr name = SQLExprParser::name();
  column->setName(name);
  column->setDataType(parseDataType());

  if (column->getDataType() != NULL && column->getDataType()->jdbcType() == Types::CHAR)
  {
    // ENUM or SET with character set.
    if (lexer->identifierEquals(FnvHash::Constants::CHARACTER))
    {
      lexer->nextToken();

      accept(Token::SET);

      if (*lexer->token() != Token::IDENTIFIER && *lexer->token() != Token::LITERAL_CHARS)
      {
        throw new ParserException(make_string_ptr(encode_util::UnicodeToUTF8(lexer->info())));
      }
      column->setCharsetExpr(primary());
    }
  }

  // May multiple collate caused by type with collate.
  while (lexer->identifierEquals(FnvHash::Constants::COLLATE))
  {
    lexer->nextToken();
    SQLExpr_ptr collateExpr;
    if (*lexer->token() == Token::IDENTIFIER)
    {
      string_ptr tmp = make_string_ptr(encode_util::UnicodeToUTF8(*lexer->stringVal()));
      collateExpr = SQLIdentifierExpr_ptr(new SQLIdentifierExpr(tmp));
    }
    else
    {
      string_ptr tmp = make_string_ptr(encode_util::UnicodeToUTF8(*lexer->stringVal()));
      collateExpr = SQLCharExpr_ptr(new SQLCharExpr(tmp));
    }
    lexer->nextToken();
    column->setCollateExpr(collateExpr);
  }

  if (lexer->identifierEquals(FnvHash::Constants::GENERATED))
  {
    lexer->nextToken();
    acceptIdentifier("ALWAYS");
    accept(Token::AS);
    accept(Token::LPAREN);
    SQLExpr_ptr expr = this->getExpr();
    accept(Token::RPAREN);
    column->setGeneratedAlawsAs(expr);
  }

  return parseColumnRest(column);
}

SQLColumnDefinition_ptr MySqlExprParser::parseColumnRest(SQLColumnDefinition_ptr column)
{
  if (*lexer->token() == Token::ON)
  {
    lexer->nextToken();
    accept(Token::UPDATE);
    SQLExpr_ptr expr = this->primary();
    column->setOnUpdate(expr);
  }

  if (lexer->identifierEquals(FnvHash::Constants::ENCODE))
  {
    lexer->nextToken();
    accept(Token::EQ);
    column->setEncode(SQLExprParser::charExpr());
  }
  if (lexer->identifierEquals(FnvHash::Constants::COMPRESSION))
  {
    lexer->nextToken();
    accept(Token::EQ);
    column->setCompression(SQLExprParser::charExpr());
  }

  if (lexer->identifierEquals(FnvHash::Constants::CHARACTER) || lexer->identifierEquals(FnvHash::Constants::CHARSET))
  {
    if (lexer->identifierEquals(FnvHash::Constants::CHARACTER))
    {
      lexer->nextToken();
      accept(Token::SET);
    }
    else
    {
      lexer->nextToken();
    }

    SQLExpr_ptr charSet;
    if (*lexer->token() == Token::IDENTIFIER)
    {
      string_ptr tmp = make_string_ptr(encode_util::UnicodeToUTF8(*lexer->stringVal()));
      charSet = SQLIdentifierExpr_ptr(new SQLIdentifierExpr(tmp));
    }
    else
    {
      string_ptr tmp = make_string_ptr(encode_util::UnicodeToUTF8(*lexer->stringVal()));
      charSet = SQLCharExpr_ptr(new SQLCharExpr(tmp));
    }
    lexer->nextToken();
    column->setCharsetExpr(charSet);

    return parseColumnRest(column);
  }
  if (lexer->identifierEquals(L"disableindex"))
  {
    lexer->nextToken();
    if (*lexer->token() == Token::XTRUE)
    {
      lexer->nextToken();
      column->setDisableIndex(BOOL::TRUE);
    }
    return parseColumnRest(column);
  }
  if (lexer->identifierEquals(L"jsonIndexAttrs"))
  {
    lexer->nextToken();
    string_ptr tmp = make_string_ptr(encode_util::UnicodeToUTF8(*lexer->stringVal()));
    column->setJsonIndexAttrsExpr(SQLIdentifierExpr_ptr(new SQLIdentifierExpr(tmp)));
    lexer->nextToken();
    return parseColumnRest(column);
  }
  if (lexer->identifierEquals(L"precision"))
  {
    lexer->nextToken();
    int precision = SQLExprParser::parseIntValue();
    acceptIdentifier("scale");
    int scale = SQLExprParser::parseIntValue();

    SQLExpr_list_ptr arguments = column->getDataType()->getArguments();
    arguments->push_back(SQLIntegerExpr_ptr(new SQLIntegerExpr(precision)));
    arguments->push_back(SQLIntegerExpr_ptr(new SQLIntegerExpr(scale)));

    return parseColumnRest(column);
  }
  if (lexer->identifierEquals(FnvHash::Constants::COLLATE))
  {
    lexer->nextToken();
    SQLExpr_ptr collateExpr;
    if (*lexer->token() == Token::IDENTIFIER)
    {
      string_ptr tmp = make_string_ptr(encode_util::UnicodeToUTF8(*lexer->stringVal()));
      collateExpr = SQLIdentifierExpr_ptr(new SQLIdentifierExpr(tmp));
    }
    else
    {
      string_ptr tmp = make_string_ptr(encode_util::UnicodeToUTF8(*lexer->stringVal()));
      collateExpr = SQLCharExpr_ptr(new SQLCharExpr(tmp));
    }
    lexer->nextToken();
    column->setCollateExpr(collateExpr);
    return parseColumnRest(column);
  }

  if (lexer->identifierEquals(FnvHash::Constants::PRECISION) && column->getDataType()->nameHashCode64() == FnvHash::Constants::DOUBLE)
  {
    lexer->nextToken();
  }

  /* Allow partition in alter table.
  if (*lexer->token() == Token::PARTITION) {
      throw new ParserException("syntax error " + lexer->info());
  }
  */

  if (lexer->identifierEquals(L"COLUMN_FORMAT"))
  {
    lexer->nextToken();
    SQLExpr_ptr expr = getExpr();
    column->setFormat(expr);
  }

  if (lexer->identifierEquals(FnvHash::Constants::STORAGE))
  {
    lexer->nextToken();
    SQLExpr_ptr expr = getExpr();
    column->setStorage(expr);
  }

  if (*lexer->token() == Token::AS)
  {
    lexer->nextToken();
    accept(Token::LPAREN);
    SQLExpr_ptr expr = getExpr();
    column->setAsExpr(expr);
    accept(Token::RPAREN);
  }

  if (lexer->identifierEquals(FnvHash::Constants::STORED) ||
      lexer->identifierEquals(L"PERSISTENT"))
  {
    lexer->nextToken();
    column->setStored(BOOL::TRUE);
  }

  if (lexer->identifierEquals(FnvHash::Constants::VIRTUAL))
  {
    lexer->nextToken();
    column->setVirtual(BOOL::TRUE);
  }

  if (lexer->identifierEquals(FnvHash::Constants::DELIMITER))
  {
    lexer->nextToken();
    SQLExpr_ptr expr = this->getExpr();
    column->setDelimiter(expr);
    return parseColumnRest(column);
  }

  if (lexer->identifierEquals(L"delimiter_tokenizer"))
  {
    lexer->nextToken();
    SQLExpr_ptr expr = this->getExpr();
    column->setDelimiterTokenizer(expr);
    return parseColumnRest(column);
  }

  if (lexer->identifierEquals(L"nlp_tokenizer"))
  {
    lexer->nextToken();
    SQLExpr_ptr expr = this->getExpr();
    column->setNlpTokenizer(expr);
  }

  if (lexer->identifierEquals(L"value_type"))
  {
    lexer->nextToken();
    SQLExpr_ptr expr = this->getExpr();
    column->setValueType(expr);
  }

  if (lexer->identifierEquals(FnvHash::Constants::COLPROPERTIES))
  {
    lexer->nextToken();
    SQLExprParser::parseAssignItem(column->getColProperties(), column);
  }

  if (lexer->identifierEquals(FnvHash::Constants::ANNINDEX))
  {
    lexer->nextToken();

    accept(Token::LPAREN);
    SQLAnnIndex_ptr annIndex = SQLAnnIndex_ptr(new SQLAnnIndex());
    for (;;)
    {
      if (lexer->identifierEquals(FnvHash::Constants::TYPE))
      {
        lexer->nextToken();
        accept(Token::EQ);
        string_ptr type = make_string_ptr(encode_util::UnicodeToUTF8(*lexer->stringVal()));
        annIndex->setIndexType(type);
        accept(Token::LITERAL_CHARS);
      }
      else if (lexer->identifierEquals(FnvHash::Constants::RTTYPE))
      {
        lexer->nextToken();
        accept(Token::EQ);
        string_ptr type = make_string_ptr(encode_util::UnicodeToUTF8(*lexer->stringVal()));
        annIndex->setRtIndexType(type);
        accept(Token::LITERAL_CHARS);
      }
      else if (lexer->identifierEquals(FnvHash::Constants::DISTANCE))
      {
        lexer->nextToken();
        accept(Token::EQ);
        string_ptr type = make_string_ptr(encode_util::UnicodeToUTF8(*lexer->stringVal()));
        annIndex->setDistance(type);
        accept(Token::LITERAL_CHARS);
      }

      if (*lexer->token() == Token::COMMA)
      {
        lexer->nextToken();
        continue;
      }

      break;
    }

    accept(Token::RPAREN);

    column->setAnnIndex(annIndex);

    return parseColumnRest(column);
  }

  SQLExprParser::parseColumnRest(column);

  return column;
}

SQLDataType_ptr MySqlExprParser::parseDataTypeRest(SQLDataType_ptr dataType)
{
  SQLExprParser::parseDataTypeRest(dataType);

  for (;;)
  {
    if (lexer->identifierEquals(FnvHash::Constants::UNSIGNED))
    {
      lexer->nextToken();
      (std::dynamic_pointer_cast<SQLDataTypeImpl>(dataType))->setUnsigned(BOOL::TRUE);
    }
    else if (lexer->identifierEquals(FnvHash::Constants::SIGNED))
    {
      lexer->nextToken(); // skip
    }
    else if (lexer->identifierEquals(FnvHash::Constants::ZEROFILL))
    {
      lexer->nextToken();
      (std::dynamic_pointer_cast<SQLDataTypeImpl>(dataType))->setZerofill(BOOL::TRUE);
    }
    else
    {
      break;
    }
  }

  if (lexer->identifierEquals(FnvHash::Constants::ARRAY))
  {
    lexer->nextToken();
    // dataType = new SQLArrayDataType(dataType);
  }

  return dataType;
}

SQLAssignItem_ptr MySqlExprParser::parseAssignItem(BOOL_ptr variant, SQLObject_ptr parent)
{
  SQLAssignItem_ptr item = SQLAssignItem_ptr(new SQLAssignItem());

  SQLExpr_ptr var = primary();

  string_ptr ident = NULL;
  long identHash = 0;
  if (variant && instanceof <SQLExpr, SQLIdentifierExpr>(var))
  {
    SQLIdentifierExpr_ptr identExpr = std::dynamic_pointer_cast<SQLIdentifierExpr>(var);
    ident = identExpr->getName();
    identHash = identExpr->hashCode64();

    if (identHash == FnvHash::Constants::GLOBAL)
    {
      ident = make_string_ptr(encode_util::UnicodeToUTF8(*lexer->stringVal()));
      lexer->nextToken();
      var = SQLVariantRefExpr_ptr(new SQLVariantRefExpr(ident, BOOL::TRUE));
    }
    else if (identHash == FnvHash::Constants::SESSION)
    {
      ident = make_string_ptr(encode_util::UnicodeToUTF8(*lexer->stringVal()));
      lexer->nextToken();
      var = SQLVariantRefExpr_ptr(new SQLVariantRefExpr(ident, BOOL::FALSE, BOOL::TRUE));
    }
    else
    {
      var = SQLVariantRefExpr_ptr(new SQLVariantRefExpr(ident));
    }
  }

  if (identHash == FnvHash::Constants::NAMES)
  {
    string_ptr charset = make_string_ptr(encode_util::UnicodeToUTF8(*lexer->stringVal()));

    SQLExpr_ptr varExpr = NULL;
    BOOL_ptr chars = BOOL::FALSE;
    Token_ptr token = lexer->token();
    if (*token == Token::IDENTIFIER)
    {
      lexer->nextToken();
    }
    else if (*token == Token::DEFAULT)
    {
      charset = make_string_ptr("DEFAULT");
      lexer->nextToken();
    }
    else if (*token == Token::QUES)
    {
      varExpr = SQLVariantRefExpr_ptr(new SQLVariantRefExpr("?"));
      lexer->nextToken();
    }
    else
    {
      chars = BOOL::TRUE;
      accept(Token::LITERAL_CHARS);
    }

    if (lexer->identifierEquals(FnvHash::Constants::COLLATE))
    {
      // MySqlCharExpr* charsetExpr = new MySqlCharExpr(charset);
      lexer->nextToken();

      string_ptr collate = make_string_ptr(encode_util::UnicodeToUTF8(*lexer->stringVal()));
      lexer->nextToken();
      // charsetExpr->setCollate(collate);

      // item->setValue(charsetExpr);
    }
    else
    {
      if (varExpr != NULL)
      {
        item->setValue(varExpr);
      }
      else
      {
        item->setValue(chars ? std::dynamic_pointer_cast<SQLExpr>(SQLCharExpr_ptr(new SQLCharExpr(charset))) : std::dynamic_pointer_cast<SQLExpr>(SQLIdentifierExpr_ptr(new SQLIdentifierExpr((charset)))));
      }
    }

    item->setTarget(var);
    return item;
  }
  else if (identHash == FnvHash::Constants::CHARACTER)
  {
    var = SQLVariantRefExpr_ptr(new SQLVariantRefExpr("CHARACTER SET"));
    accept(Token::SET);
    if (*lexer->token() == Token::EQ)
    {
      lexer->nextToken();
    }
  }
  else if (identHash == FnvHash::Constants::CHARSET)
  {
    var = SQLVariantRefExpr_ptr(new SQLVariantRefExpr("CHARACTER SET"));
    if (*lexer->token() == Token::EQ)
    {
      lexer->nextToken();
    }
  }
  else if (identHash == FnvHash::Constants::TRANSACTION)
  {
    var = SQLVariantRefExpr_ptr(new SQLVariantRefExpr("TRANSACTION"));
    if (*lexer->token() == Token::EQ)
    {
      lexer->nextToken();
    }
  }
  else
  {
    if (*lexer->token() == Token::COLONEQ)
    {
      lexer->nextToken();
    }
    else
    {
      accept(Token::EQ);
    }
  }

  if (*lexer->token() == Token::ON)
  {
    lexer->nextToken();
    string_ptr tmp = make_string_ptr("ON");
    item->setValue(SQLIdentifierExpr_ptr(new SQLIdentifierExpr((tmp))));
  }
  else
  {
    item->setValue(this->getExpr());
  }

  item->setTarget(var);
  return item;
}

SQLName_ptr MySqlExprParser::nameRest(SQLName_ptr name)
{
  if (*lexer->token() == Token::VARIANT &&
      (L"@" == *lexer->stringVal()))
  {
    lexer->nextToken();
    // MySqlUserName* userName = new MySqlUserName();
    // userName.setUserName(((SQLIdentifierExpr)name).getName());

    // if (*lexer->token() == Token::LITERAL_CHARS)
    // {
    //   userName.setHost("'" + lexer->stringVal() + "'");
    // }
    // else
    // {
    //   userName.setHost(lexer->stringVal());
    // }
    lexer->nextToken();

    if (lexer->identifierEquals(FnvHash::Constants::IDENTIFIED))
    {
      lexer->nextToken();
      accept(Token::BY);
      // userName.setIdentifiedBy(lexer->stringVal());
      lexer->nextToken();
    }

    // return userName;
  }
  return SQLExprParser::nameRest(name);
}

// MySqlPrimaryKey *MySqlExprParser::parsePrimaryKey()
// {
// MySqlPrimaryKey* primaryKey = new MySqlPrimaryKey();
// parseIndex(primaryKey->getIndexDefinition());
// return primaryKey;
/*
accept(Token::PRIMARY);
accept(Token::KEY);

MySqlPrimaryKey primaryKey = new MySqlPrimaryKey();

if (lexer->identifierEquals(FnvHash::Constants::USING)) {
    lexer->nextToken();
    primaryKey.setIndexType(lexer->stringVal());
    lexer->nextToken();
}

if (*lexer->token() != Token::LPAREN) {
    SQLName name = this->name();
    primaryKey.setName(name);
}

accept(Token::LPAREN);
for (;;) {
    setAllowIdentifierMethod(BOOL::FALSE);

    SQLExpr_ptr expr;
    if (*lexer->token() == Token::LITERAL_ALIAS) {
        expr = this->name();
    } else {
        expr = this->expr();
    }

    setAllowIdentifierMethod(BOOL::TRUE);

    SQLSelectOrderByItem item = new SQLSelectOrderByItem();

    item.setExpr(expr);

    if (*lexer->token() == Token::ASC) {
        lexer->nextToken();
        item.setType(SQLOrderingSpecification.ASC);
    } else if (*lexer->token() == Token::DESC) {
        lexer->nextToken();
        item.setType(SQLOrderingSpecification.DESC);
    }

    primaryKey.addColumn(item);
    if (!(*lexer->token() == (Token::COMMA))) {
        break;
    } else {
        lexer->nextToken();
    }
}
accept(Token::RPAREN);

for (;;) {
    if (*lexer->token() == Token::COMMENT) {
        lexer->nextToken();
        SQLExpr_ptr comment = this->primary();
        primaryKey.setComment(comment);
    } else if (lexer->identifierEquals(FnvHash::Constants::KEY_BLOCK_SIZE)) {
        lexer->nextToken();
        if (*lexer->token() == Token::EQ) {
            lexer->nextToken();
        }
        SQLExpr_ptr keyBlockSize = this->primary();
        primaryKey.setKeyBlockSize(keyBlockSize);
    } else if (lexer->identifierEquals(FnvHash::Constants::USING)) {
        lexer->nextToken();
        primaryKey.setIndexType(lexer->stringVal());
        accept(Token::IDENTIFIER);
    } else {
        break;
    }
}

return primaryKey;
*/
// }

// MySqlUnique *MySqlExprParser::parseUnique()
// {
// MySqlUnique* unique = new MySqlUnique();
// parseIndex(unique.getIndexDefinition());
// return unique;
/*
accept(Token::UNIQUE);

if (*lexer->token() == Token::KEY) {
    lexer->nextToken();
}

if (*lexer->token() == Token::INDEX) {
    lexer->nextToken();
}

MySqlUnique unique = new MySqlUnique();

if (*lexer->token() != Token::LPAREN && !lexer->identifierEquals(FnvHash::Constants::USING)) {
    SQLName indexName = name();
    unique.setName(indexName);
}

//5.5语法 USING BTREE 放在index 名字后
if (lexer->identifierEquals(FnvHash::Constants::USING)) {
    lexer->nextToken();
    unique.setIndexType(lexer->stringVal());
    lexer->nextToken();
}

parseIndexRest(unique);

for (;;) {
    if (*lexer->token() == Token::COMMENT) {
        lexer->nextToken();
        SQLExpr_ptr comment = this->primary();
        unique.setComment(comment);
    } else if (lexer->identifierEquals(FnvHash::Constants::KEY_BLOCK_SIZE)) {
        lexer->nextToken();
        if (*lexer->token() == Token::EQ) {
            lexer->nextToken();
        }
        SQLExpr_ptr keyBlockSize = this->primary();
        unique.setKeyBlockSize(keyBlockSize);
    } else if (lexer->identifierEquals(FnvHash::Constants::USING)) {
        lexer->nextToken();
        unique.setIndexType(lexer->stringVal());
        accept(Token::IDENTIFIER);
    } else {
        break;
    }
}

return unique;
*/
// }

// MysqlForeignKey *MySqlExprParser::parseForeignKey()
// {
//   accept(Token::FOREIGN);
//   accept(Token::KEY);

//   MysqlForeignKey fk = new MysqlForeignKey();

//   if (*lexer->token() != Token::LPAREN)
//   {
//     SQLName indexName = name();
//     fk.setIndexName(indexName);
//   }

//   accept(Token::LPAREN);
//   this->names(fk.getReferencingColumns(), fk);
//   accept(Token::RPAREN);

//   accept(Token::REFERENCES);

//   fk.setReferencedTableName(this->name());

//   accept(Token::LPAREN);
//   this->names(fk.getReferencedColumns());
//   accept(Token::RPAREN);

//   if (lexer->identifierEquals(FnvHash::Constants::MATCH))
//   {
//     lexer->nextToken();
//     if (lexer->identifierEquals("FULL") || lexer->token() == Token::FULL)
//     {
//       fk.setReferenceMatch(Match.FULL);
//       lexer->nextToken();
//     }
//     else if (lexer->identifierEquals(FnvHash::Constants::PARTIAL))
//     {
//       fk.setReferenceMatch(Match.PARTIAL);
//       lexer->nextToken();
//     }
//     else if (lexer->identifierEquals(FnvHash::Constants::SIMPLE))
//     {
//       fk.setReferenceMatch(Match.SIMPLE);
//       lexer->nextToken();
//     }
//     else
//     {
//       throw new ParserException("TODO : " + lexer->info());
//     }
//   }

//   while (*lexer->token() == Token::ON)
//   {
//     lexer->nextToken();

//     if (*lexer->token() == Token::DELETE)
//     {
//       lexer->nextToken();

//       Option option = parseReferenceOption();
//       fk.setOnDelete(option);
//     }
//     else if (*lexer->token() == Token::UPDATE)
//     {
//       lexer->nextToken();

//       Option option = parseReferenceOption();
//       fk.setOnUpdate(option);
//     }
//     else
//     {
//       throw new ParserException("syntax error, expect DELETE or UPDATE, actual " + lexer->token() + " " + lexer->info());
//     }
//   }
//   return fk;
// }

SQLAggregateExpr_ptr MySqlExprParser::parseAggregateExprRest(SQLAggregateExpr_ptr aggregateExpr)
{
  if (*lexer->token() == Token::ORDER)
  {
    SQLOrderBy_ptr orderBy = this->parseOrderBy();
    aggregateExpr->setOrderBy(orderBy);
    // 为了兼容之前的逻辑
    aggregateExpr->putAttribute("ORDER BY", orderBy);
  }
  if (lexer->identifierEquals(FnvHash::Constants::SEPARATOR))
  {
    lexer->nextToken();

    SQLExpr_ptr seperator = this->primary();
    seperator->setParent(aggregateExpr);

    aggregateExpr->putAttribute("SEPARATOR", seperator);
  }
  return aggregateExpr;
}

// MySqlOrderingExpr *MySqlExprParser::parseSelectGroupByItem()
// {
//   MySqlOrderingExpr* item = new MySqlOrderingExpr();

//   item->setExpr(getExpr());

//   if (*lexer->token() == Token::ASC)
//   {
//     lexer->nextToken();
//     item.setType(SQLOrderingSpecification::ASC);
//   }
//   else if (*lexer->token() == Token::DESC)
//   {
//     lexer->nextToken();
//     item.setType(SQLOrderingSpecification::DESC);
//   }

//   return item;
// }

SQLSubPartition_ptr MySqlExprParser::parseSubPartition()
{
  SQLSubPartition_ptr subPartition = SQLSubPartition_ptr(new SQLSubPartition());
  subPartition->setName(this->name());

  for (;;)
  {
    BOOL_ptr storage = BOOL::FALSE;
    if (lexer->identifierEquals(FnvHash::Constants::DATA))
    {
      lexer->nextToken();
      acceptIdentifier("DIRECTORY");
      if (*lexer->token() == Token::EQ)
      {
        lexer->nextToken();
      }
      subPartition->setDataDirectory(this->getExpr());
    }
    else if (*lexer->token() == Token::TABLESPACE)
    {
      lexer->nextToken();
      if (*lexer->token() == Token::EQ)
      {
        lexer->nextToken();
      }
      SQLName_ptr tableSpace = this->name();
      subPartition->setTablespace(tableSpace);
    }
    else if (*lexer->token() == Token::INDEX)
    {
      lexer->nextToken();
      acceptIdentifier("DIRECTORY");
      if (*lexer->token() == Token::EQ)
      {
        lexer->nextToken();
      }
      subPartition->setIndexDirectory(this->expr());
    }
    else if (lexer->identifierEquals(FnvHash::Constants::MAX_ROWS))
    {
      lexer->nextToken();
      if (*lexer->token() == Token::EQ)
      {
        lexer->nextToken();
      }
      SQLExpr_ptr maxRows = this->primary();
      subPartition->setMaxRows(maxRows);
    }
    else if (lexer->identifierEquals(FnvHash::Constants::MIN_ROWS))
    {
      lexer->nextToken();
      if (*lexer->token() == Token::EQ)
      {
        lexer->nextToken();
      }
      SQLExpr_ptr minRows = this->primary();
      subPartition->setMinRows(minRows);
    }
    else if (lexer->identifierEquals(FnvHash::Constants::ENGINE) || //
             (storage = (*lexer->token() == Token::STORAGE ||
                         lexer->identifierEquals(FnvHash::Constants::STORAGE))
                            ? BOOL::TRUE
                            : BOOL::FALSE))
    {
      if (storage)
      {
        lexer->nextToken();
      }
      acceptIdentifier("ENGINE");

      if (*lexer->token() == Token::EQ)
      {
        lexer->nextToken();
      }

      SQLName_ptr engine = this->name();
      subPartition->setEngine(engine);
    }
    else if (*lexer->token() == Token::COMMENT)
    {
      lexer->nextToken();
      if (*lexer->token() == Token::EQ)
      {
        lexer->nextToken();
      }
      SQLExpr_ptr comment = this->primary();
      subPartition->setComment(comment);
    }
    else
    {
      break;
    }
  }

  return subPartition;
}

SQLPartition_ptr MySqlExprParser::parsePartition()
{
  if (lexer->identifierEquals(FnvHash::Constants::DBPARTITION) ||
      lexer->identifierEquals(FnvHash::Constants::TBPARTITION) ||
      lexer->identifierEquals(FnvHash::Constants::SUBPARTITION))
  {
    lexer->nextToken();
  }
  else
  {
    accept(Token::PARTITION);
  }

  SQLPartition_ptr partitionDef = SQLPartition_ptr(new SQLPartition());

  SQLName_ptr name;
  if (*lexer->token() == Token::LITERAL_INT)
  {
    long number = lexer->integerValue();
    name = SQLIdentifierExpr_ptr(new SQLIdentifierExpr(make_string_ptr(std::to_string(number))));
    lexer->nextToken();
  }
  else
  {
    name = this->name();
  }
  partitionDef->setName(name);

  SQLPartitionValue_ptr values = this->parsePartitionValues();
  if (values != NULL)
  {
    partitionDef->setValues(values);
  }

  for (;;)
  {
    BOOL_ptr storage = BOOL::FALSE;
    if (lexer->identifierEquals(FnvHash::Constants::DATA))
    {
      lexer->nextToken();
      acceptIdentifier("DIRECTORY");
      if (*lexer->token() == Token::EQ)
      {
        lexer->nextToken();
      }
      partitionDef->setDataDirectory(this->expr());
    }
    else if (*lexer->token() == Token::TABLESPACE)
    {
      lexer->nextToken();
      if (*lexer->token() == Token::EQ)
      {
        lexer->nextToken();
      }
      SQLName_ptr tableSpace = this->name();
      partitionDef->setTablespace(tableSpace);
    }
    else if (*lexer->token() == Token::INDEX)
    {
      lexer->nextToken();
      acceptIdentifier("DIRECTORY");
      if (*lexer->token() == Token::EQ)
      {
        lexer->nextToken();
      }
      partitionDef->setIndexDirectory(this->expr());
    }
    else if (lexer->identifierEquals(FnvHash::Constants::MAX_ROWS))
    {
      lexer->nextToken();
      if (*lexer->token() == Token::EQ)
      {
        lexer->nextToken();
      }
      SQLExpr_ptr maxRows = this->primary();
      partitionDef->setMaxRows(maxRows);
    }
    else if (lexer->identifierEquals(FnvHash::Constants::MIN_ROWS))
    {
      lexer->nextToken();
      if (*lexer->token() == Token::EQ)
      {
        lexer->nextToken();
      }
      SQLExpr_ptr minRows = this->primary();
      partitionDef->setMaxRows(minRows);
    }
    else if (lexer->identifierEquals(FnvHash::Constants::ENGINE) || //
             (storage = (*lexer->token() == Token::STORAGE ||
                         lexer->identifierEquals(FnvHash::Constants::STORAGE))
                            ? BOOL::TRUE
                            : BOOL::FALSE))
    {
      if (storage)
      {
        lexer->nextToken();
      }
      acceptIdentifier("ENGINE");

      if (*lexer->token() == Token::EQ)
      {
        lexer->nextToken();
      }

      SQLName_ptr engine = this->name();
      partitionDef->setEngine(SQLName_ptr(engine));
    }
    else if (*lexer->token() == Token::COMMENT)
    {
      lexer->nextToken();
      if (*lexer->token() == Token::EQ)
      {
        lexer->nextToken();
      }
      SQLExpr_ptr comment = this->primary();
      partitionDef->setComment(comment);
    }
    else
    {
      break;
    }
  }

  if (*lexer->token() == Token::LPAREN)
  {
    lexer->nextToken();

    for (;;)
    {
      acceptIdentifier("SUBPARTITION");

      SQLSubPartition_ptr subPartition(parseSubPartition());

      partitionDef->addSubPartition(subPartition);

      if (*lexer->token() == Token::COMMA)
      {
        lexer->nextToken();
        continue;
      }
      break;
    }

    accept(Token::RPAREN);
  }

  if (lexer->identifierEquals(L"LOCALITY"))
  {
    lexer->nextToken();
    accept(Token::EQ);
    SQLExpr_ptr locality = this->expr();
    partitionDef->setLocality(locality);
  }

  return partitionDef;
}

SQLExpr_ptr MySqlExprParser::parseAliasExpr(string_ptr alias)
{
  if (isEnabled(SQLParserFeature_ptr(new SQLParserFeature(&SQLParserFeature::KeepNameQuotes))))
  {
    return SQLIdentifierExpr_ptr(new SQLIdentifierExpr((alias)));
  }
  Lexer_ptr newLexer = Lexer_ptr(new Lexer(encode_util::UTF8ToUnicode(*alias)));
  newLexer->nextTokenValue();
  string_ptr tmp = make_string_ptr(encode_util::UnicodeToUTF8(*newLexer->stringVal()));
  return SQLCharExpr_ptr(new SQLCharExpr(tmp));
}

BOOL_ptr MySqlExprParser::parseTableOptions(SQLAssignItem_list_ptr assignItems, SQLDDLStatement_ptr parent)
{
  // Check whether table options.
  BOOL_ptr succeed = BOOL::FALSE;

  while (*lexer->token() != Token::XEOF)
  {
    long hash = lexer->hashLCase();
    // int idx = Arrays.binarySearch(MySqlExprParser::SINGLE_WORD_TABLE_OPTIONS_CODES, hash);
    // int idx = std::find(MySqlExprParser::SINGLE_WORD_TABLE_OPTIONS_CODES, MySqlExprParser::SINGLE_WORD_TABLE_OPTIONS_CODES + sizeof(MySqlExprParser::SINGLE_WORD_TABLE_OPTIONS_CODES) / sizeof(long), hash) - MySqlExprParser::SINGLE_WORD_TABLE_OPTIONS_CODES;
    int idx = -1;
    int size = MySqlExprParser::SINGLE_WORD_TABLE_OPTIONS_CODES->size();
    for (int i = 0; i < size; i++)
    {
      LOG_INFO << __FUNCTION__ << ": i:" << i << ",size:" << size;
      if (SQLExprParser::AGGREGATE_FUNCTIONS_CODES->at(i) == hash)
      {
        idx = i;
        break;
      }
    }

    SQLAssignItem_ptr assignItem;
    Lexer_SavePoint_ptr mark;

    if (idx >= 0 &&
        idx < (sizeof(MySqlExprParser::SINGLE_WORD_TABLE_OPTIONS_CODES) / sizeof(long)) &&
        SINGLE_WORD_TABLE_OPTIONS_CODES->at(idx) == hash &&
        (*lexer->token() == Token::IDENTIFIER ||
         (!(lexer->token()->name->empty()) &&
          lexer->token()->name->length() == SINGLE_WORD_TABLE_OPTIONS->at(idx)->length())))
    {
      // Special items.
      if (*lexer->token() == Token::TABLESPACE)
      {
        lexer->nextToken();

        // MySqlCreateTableStatement::TableSpaceOption* option = new MySqlCreateTableStatement::TableSpaceOption();
        // option.setName(name());

        if (lexer->identifierEquals(L"STORAGE"))
        {
          lexer->nextToken();
          // option.setStorage(name());
        }
        // assignItem = new SQLAssignItem(new SQLIdentifierExpr("TABLESPACE"), option);
      }
      else if (*lexer->token() == Token::UNION)
      {
        lexer->nextToken();
        if (*lexer->token() == Token::EQ)
        {
          lexer->nextToken();
        }

        accept(Token::LPAREN);
        SQLListExpr_ptr list_ = SQLListExpr_ptr(new SQLListExpr());
        exprList(list_->getItems(), list_);
        accept(Token::RPAREN);
        string_ptr tmp = make_string_ptr("UNION");
        assignItem = SQLAssignItem_ptr(new SQLAssignItem(SQLIdentifierExpr_ptr(new SQLIdentifierExpr((tmp))), list_));
      }
      else if (lexer->identifierEquals(L"PACK_KEYS"))
      {
        // Caution: Not in MySql documents.
        lexer->nextToken();
        if (*lexer->token() == Token::EQ)
        {
          lexer->nextToken();
        }

        if (lexer->identifierEquals(L"PACK"))
        {
          lexer->nextToken();
          accept(Token::ALL);
          string_ptr tmp1 = make_string_ptr("PACK_KEYS");
          string_ptr tmp2 = make_string_ptr("PACK ALL");
          assignItem = SQLAssignItem_ptr(new SQLAssignItem(SQLIdentifierExpr_ptr(new SQLIdentifierExpr(tmp1)), SQLIdentifierExpr_ptr(new SQLIdentifierExpr(tmp2))));
        }
        else
        {
          string_ptr tmp1 = make_string_ptr("PACK_KEYS");
          assignItem = SQLAssignItem_ptr(new SQLAssignItem(SQLIdentifierExpr_ptr(new SQLIdentifierExpr(tmp1)), getExpr()));
        }
      }
      else if (lexer->identifierEquals(FnvHash::Constants::ENGINE))
      {
        lexer->nextToken();
        if (*lexer->token() == Token::EQ)
        {
          lexer->nextToken();
        }

        SQLExpr_ptr expr;
        if (*lexer->token() == Token::MERGE)
        {
          string_ptr tmp = make_string_ptr(encode_util::UnicodeToUTF8(*lexer->stringVal()));
          expr = SQLIdentifierExpr_ptr(new SQLIdentifierExpr(tmp));
          lexer->nextToken();
        }
        else
        {
          expr = getExpr();
        }
        string_ptr tmp = make_string_ptr("ENGINE");
        assignItem = SQLAssignItem_ptr(new SQLAssignItem(SQLIdentifierExpr_ptr(new SQLIdentifierExpr(tmp)), expr));
      }
      else
      {
        // Find single key, store as KV.
        lexer->nextToken();
        if (*lexer->token() == Token::EQ)
        {
          lexer->nextToken();
        }

        // STORAGE_POLICY
        assignItem = SQLAssignItem_ptr(new SQLAssignItem(
            SQLIdentifierExpr_ptr(new SQLIdentifierExpr((SINGLE_WORD_TABLE_OPTIONS->at(idx)))),
            idx == 9 ? SQLCharExpr_ptr(charExpr()) : getExpr()));
      }
    }
    else
    {
      // Following may not table options. Save mark.
      mark = lexer->mark();

      if (*lexer->token() == Token::DEFAULT)
      {
        // [DEFAULT] CHARACTER SET [=] charset_name
        // [DEFAULT] COLLATE [=] collation_name
        lexer->nextToken();
        if (lexer->identifierEquals(FnvHash::Constants::CHARACTER))
        {
          lexer->nextToken();
          accept(Token::SET);
          if (*lexer->token() == Token::EQ)
          {
            lexer->nextToken();
          }
          string_ptr tmp = make_string_ptr("CHARACTER SET");
          assignItem = SQLAssignItem_ptr(new SQLAssignItem(SQLIdentifierExpr_ptr(new SQLIdentifierExpr(tmp)), getExpr()));
        }
        else if (lexer->identifierEquals(FnvHash::Constants::COLLATE))
        {
          lexer->nextToken();
          if (*lexer->token() == Token::EQ)
          {
            lexer->nextToken();
          }
          string_ptr tmp = make_string_ptr("COLLATE");
          assignItem = SQLAssignItem_ptr(new SQLAssignItem(SQLIdentifierExpr_ptr(new SQLIdentifierExpr(tmp)), getExpr()));
        }
      }
      else if (hash == FnvHash::Constants::CHARACTER)
      {
        // CHARACTER SET [=] charset_name
        lexer->nextToken();
        accept(Token::SET);
        if (*lexer->token() == Token::EQ)
        {
          lexer->nextToken();
        }
        string_ptr tmp = make_string_ptr("CHARACTER SET");
        assignItem = SQLAssignItem_ptr(new SQLAssignItem(SQLIdentifierExpr_ptr(new SQLIdentifierExpr(tmp)), getExpr()));
      }
      else if (hash == FnvHash::Constants::DATA ||
               *lexer->token() == Token::INDEX)
      {
        // {DATA|INDEX} DIRECTORY [=] 'absolute path to directory'
        lexer->nextToken();
        if (lexer->identifierEquals(L"DIRECTORY"))
        {
          lexer->nextToken();
          if (*lexer->token() == Token::EQ)
          {
            lexer->nextToken();
          }
          string_ptr tmp = make_string_ptr("COLLATE");
          assignItem = SQLAssignItem_ptr(new SQLAssignItem(SQLIdentifierExpr_ptr(new SQLIdentifierExpr(tmp)), getExpr()));
        }
      }
    }

    if (assignItem != NULL)
    {
      assignItem->setParent(parent);
      assignItems->push_back(assignItem);
      succeed = BOOL::TRUE;
    }
    else
    {
      if (mark != NULL)
      {
        lexer->reset(mark);
      }
      return succeed;
    }

    // Optional comma.
    if (*lexer->token() == Token::COMMA)
    {
      lexer->nextToken();
    }
    else if (*lexer->token() == Token::XEOF)
    {
      break;
    }
  }
  return succeed;
}
